#region Header

// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Shinsaku Nakagawa" email="shinsaku@users.sourceforge.jp"/>
//     <version>$Revision: 2561 $</version>
// </file>

#endregion Header

namespace ICSharpCode.TextEditor
{
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;

    /// <summary>
    /// Used internally, not for own use.
    /// </summary>
    internal class Ime
    {
        #region Fields

        private const int CFS_POINT = 0x0002;
        private const int IMC_SETCOMPOSITIONFONT = 0x000a;
        private const int IMC_SETCOMPOSITIONWINDOW = 0x000c;
        private const int WM_IME_CONTROL = 0x0283;

        static bool disableIME;

        private Font font = null;
        private IntPtr hIMEWnd;
        private IntPtr hWnd;
        LOGFONT lf = null;

        #endregion Fields

        #region Constructors

        public Ime(IntPtr hWnd, Font font)
        {
            string PROCESSOR_ARCHITEW6432 = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432");
            if (PROCESSOR_ARCHITEW6432 == "IA64" || PROCESSOR_ARCHITEW6432 == "AMD64") {
                disableIME = true;
            }
            this.hWnd = hWnd;
            this.hIMEWnd = ImmGetDefaultIMEWnd(hWnd);
            this.font = font;
            SetIMEWindowFont(font);
        }

        #endregion Constructors

        #region Properties

        public Font Font
        {
            get {
                return font;
            }
            set {
                if (!value.Equals(font)) {
                    font = value;
                    lf = null;
                    SetIMEWindowFont(value);
                }
            }
        }

        public IntPtr HWnd
        {
            set {
                if (this.hWnd != value) {
                    this.hWnd = value;
                    this.hIMEWnd = ImmGetDefaultIMEWnd(value);
                    SetIMEWindowFont(font);
                }
            }
        }

        #endregion Properties

        #region Methods

        public void SetIMEWindowLocation(int x, int y)
        {
            if (disableIME || hIMEWnd == IntPtr.Zero) return;

            POINT p = new POINT();
            p.x = x;
            p.y = y;

            COMPOSITIONFORM lParam = new COMPOSITIONFORM();
            lParam.dwStyle = CFS_POINT;
            lParam.ptCurrentPos = p;
            lParam.rcArea = new RECT();

            try {
                SendMessage(
                    hIMEWnd,
                    WM_IME_CONTROL,
                    new IntPtr(IMC_SETCOMPOSITIONWINDOW),
                    lParam
                );
            } catch (AccessViolationException ex) {
                Handle(ex);
            }
        }

        [DllImport("imm32.dll")]
        private static extern IntPtr ImmGetDefaultIMEWnd(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, COMPOSITIONFORM lParam);

        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, [In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lParam);

        void Handle(Exception ex)
        {
            Console.WriteLine(ex);
            if (!disableIME) {
                disableIME = true;
                MessageBox.Show("Error calling IME: " + ex.Message + "\nIME is disabled.", "IME error");
            }
        }

        private void SetIMEWindowFont(Font f)
        {
            if (disableIME || hIMEWnd == IntPtr.Zero) return;

            if (lf == null) {
                lf = new LOGFONT();
                f.ToLogFont(lf);
                lf.lfFaceName = f.Name;  // This is very important! "Font.ToLogFont" Method sets invalid value to LOGFONT.lfFaceName
            }

            try {
                SendMessage(
                    hIMEWnd,
                    WM_IME_CONTROL,
                    new IntPtr(IMC_SETCOMPOSITIONFONT),
                    lf
                );
            } catch (AccessViolationException ex) {
                Handle(ex);
            }
        }

        #endregion Methods

        #region Nested Types

        [StructLayout(LayoutKind.Sequential)]
        private class COMPOSITIONFORM
        {
            public int dwStyle = 0;
            public POINT ptCurrentPos = null;
            public RECT rcArea = null;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class LOGFONT
        {
            public int lfHeight = 0;
            public int lfWidth = 0;
            public int lfEscapement = 0;
            public int lfOrientation = 0;
            public int lfWeight = 0;
            public byte lfItalic = 0;
            public byte lfUnderline = 0;
            public byte lfStrikeOut = 0;
            public byte lfCharSet = 0;
            public byte lfOutPrecision = 0;
            public byte lfClipPrecision = 0;
            public byte lfQuality = 0;
            public byte lfPitchAndFamily = 0;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
            public string lfFaceName = null;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class POINT
        {
            public int x = 0;
            public int y = 0;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class RECT
        {
            public int left = 0;
            public int top = 0;
            public int right = 0;
            public int bottom = 0;
        }

        #endregion Nested Types
    }
}